package com.xyz.graph.service;


import com.xyz.graph.common.CommonResult;
import com.xyz.graph.exception.BuiltinSearchException;
import com.xyz.graph.util.PathUtil;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.*;


@Component
public class BuiltinSearchOperation {

    private static Logger logger = LoggerFactory.getLogger(BuiltinSearchOperation.class);

    /**
     * id是value，field是key，field=id，例如personId=6541320000
     * @param id  实体的id
     * @param step  实体扩展阶数
     * @param relationTypes 实体扩展的关系类型
     * @param field 用于搜索实体的Field
     * */
    public List<ArrayList<HashMap<String,String>>> entityExtend(GraphTraversalSource g,
                                                                String id,
                                                                int step,
                                                                String[] relationTypes,
                                                                String field,
                                                                boolean allData){
        if(step < 1 || step > 5){
            throw new BuiltinSearchException(CommonResult.PARAM_ERROR,"--实体扩展：阶数只能是{1,2,3,4,5}中的一个值");
        }
        Vertex v = getVertexByKV(g,field,id);

        GraphTraversal<Vertex, Vertex> traversal = g.V(v);

        if(relationTypes == null  || relationTypes.length < 1){
            //进行所有关系类型上的实体拓展
            for(int i=0;i<step;i++){
                traversal.bothE().bothV();
            }
        }else if(relationTypes.length == 1){
            //进行特定关系类型上的实体拓展
            for(int i=0;i<step;i++) {
                traversal.bothE(relationTypes[0]).bothV();
            }
        }else {
            String label = relationTypes[0];
            String[] othersLables = new String[relationTypes.length - 1];
            System.arraycopy(relationTypes,1,othersLables,0,othersLables.length);

            for(int i=0;i<step;i++){
                traversal.bothE().hasLabel(label,othersLables).bothV();
            }
        }
        GraphTraversal<Vertex, Path> paths = traversal.simplePath().path();
        return PathUtil.pathAnalyze(paths,g,allData);
    }

    /**
     * @param  id1  实体1的ID
     * @param  id2  实体2的ID
     * @param  step 关系推演的阶数
     * */
    public List<ArrayList<HashMap<String,String>>> relationDeduce(GraphTraversalSource g,
                                                                  String id1,
                                                                  String field1,
                                                                  String id2,
                                                                  String field2,
                                                                  int step,
                                                                  boolean allData){
        if(step < 1 || step > 4){
            throw new BuiltinSearchException(CommonResult.PARAM_ERROR,"--关系推演：阶数只能是{1,2,3,4}中的一个值");
        }
        //根据实体ID获取星图中的VertexID
        Vertex v1 = getVertexByKV(g,field1,id1);
//        Vertex v2 = getVertexByKV(g,field2,id2);
        GraphTraversal<Vertex, Vertex> traversal = g.V(v1);
        for(int i=0;i<step;i++){
            traversal.bothE().bothV();
        }
        GraphTraversal<Vertex, Path> paths = traversal.has(field2,id2).simplePath().path();
        /*GraphTraversal<Vertex, Path> paths = traversal.hasId(list2.get(0)).simplePath().path();*/
        /*GraphTraversal<Vertex, Path> paths = traversal.V(list2.get(0)).simplePath().path();*/
        List<ArrayList<HashMap<String,String>>> list = PathUtil.pathAnalyze(paths,g,allData);
        return list;
    }

    /**
     * 获得所有邻居的共同邻居实体
     * @param g 图连接
     * @param fields 实体对应到图中节点的属性名
     * @param vertexIds 实体
     * @return 所有共同邻居实体到各待查询实体路径的列表
     */
    public List<ArrayList<HashMap<String, String>>> getCommonNeighborsAll(
            GraphTraversalSource g, List<String> fields, List<String> vertexIds) throws Exception {
        int numVertices = vertexIds.size();
        int i;

        logger.info("--通过id获取所有点");
        List<Vertex> vertices = new ArrayList<>(numVertices);
        for ( i = 0; i < numVertices; i++ ) {
            Vertex vertex = getVertexByKV(g, fields.get(i), vertexIds.get(i));
            vertices.add(vertex);
        }

        logger.info("--求取所有共同邻居节点");
        Set<Vertex> commonVertices = new HashSet<>();
        GraphTraversal<Vertex, Vertex> vertexGraphTraversal = g.V(vertices.get(0)).both();
        while (vertexGraphTraversal.hasNext() ) {
            commonVertices.add(vertexGraphTraversal.next());
        }
        Set<Vertex> commonVerticesTemp = new HashSet<>();
        for ( i = 1; i < numVertices; i++ ) {
            vertexGraphTraversal = g.V(vertices.get(i)).both();
            while ( vertexGraphTraversal.hasNext() ) {
                Vertex vertex = vertexGraphTraversal.next();
                if ( commonVertices.contains(vertex) ) {
                    commonVerticesTemp.add(vertex);
                }
            }
            commonVertices.clear();
            commonVertices.addAll(commonVerticesTemp);
            commonVerticesTemp.clear();
        }

        logger.info("--求取所有从共同邻居到待查询节点路径");
        Iterator<Vertex> iter = commonVertices.iterator();
        List<ArrayList<HashMap<String, String>>> commonNeighPaths = new ArrayList<>();
        boolean isAllData = true;
        while ( iter.hasNext() ) {
            Vertex vertex = iter.next();
            GraphTraversal<Vertex, Path> pathGraphTraversal = g.V(vertex).bothE().bothV().simplePath().path();
            List<ArrayList<HashMap<String,String>>> paths = PathUtil.pathAnalyze(pathGraphTraversal, g, isAllData);
            for ( ArrayList<HashMap<String, String>> path : paths ) {
                for ( i = 0; i < numVertices; i++ ) {
                    if ( path.size() != 3 ) {
                        throw new Exception("--获取路径长度有误，应为3（共同邻居节点，边，边的另一端节点），实际得到"
                                + path.size());
                    }
                    HashMap<String, String> pathTail = path.get(2);
                    if ( pathTail.containsKey(fields.get(i)) && pathTail.get(fields.get(i)).equals(vertexIds.get(i)) ) {
                        commonNeighPaths.add(path);
                        break;
                    }
                }
            }
        }
        return commonNeighPaths;
    }

    public List<ArrayList<HashMap<String, String>>> getCommonNeighborsAllOld(
            GraphTraversalSource g, List<String> fields, List<String> vertexIds) throws Exception {
        int numVertices = vertexIds.size();
        int numPairs = (numVertices + 1) / 2;
        int i, j, k;

        List<ArrayList<HashMap<String, String>>> commonEntInPair = new ArrayList<>();
        ArrayList<HashMap<String, String>> commonEntInPairHere;
        List<ArrayList<HashMap<String, String>>> commonTmp;

        // 得到两两实体的关系推演结果
        for ( i = 0; i < numVertices / 2; i++ ) {
            commonTmp = relationDeduce(g, vertexIds.get(2 * i), fields.get(2 * i),
                    vertexIds.get(2 * i + 1), fields.get(2 * i + 1), 2, true);
            if ( commonTmp == null ) {
                throw new Exception("--关系推演错误");
            } else if ( commonTmp.size() == 0 ) {
                return new ArrayList<>();
            } else {
                commonEntInPairHere = getEntityFromRelDed(commonTmp);
                commonEntInPair.add(commonEntInPairHere);
            }
        }
        // 如果待查询实体个数为奇数，最后一个实体与第一个实体配对做关系推演
        if ( numVertices % 2 == 1 ) {
            commonTmp = relationDeduce(g, vertexIds.get(2 * i), fields.get(2 * i),
                    vertexIds.get(2 * i + 1), fields.get(2 * i + 1), 2, true);
            if ( commonTmp == null ) {
                throw new Exception("--关系推演错误");
            } else if ( commonTmp.size() == 0 ) {
                return new ArrayList<>();
            } else {
                commonEntInPairHere = getEntityFromRelDed(commonTmp);
                commonEntInPair.add(commonEntInPairHere);
            }
        }

        // 如果组成的实体对大于1，说明前面得到的结果需要求交集
        while ( numPairs > 1 ) {
            for ( i = 0; i < numPairs / 2; i++ ) {
                commonEntInPair.set(i, getCommon(commonEntInPair.get(2 * i), commonEntInPair.get(2 * i + 1)));
                if ( commonEntInPair.get(i).size() == 0 ) {
                    return new ArrayList<>();
                }
            }
            if ( numPairs % 2 == 1 ) {
                numPairs = (numPairs + 1) / 2;
                commonEntInPair.set(numPairs - 1, commonEntInPair.get(2 * numPairs - 2));
            } else { numPairs /= 2; }
        }

        // 得到所有共同邻居实体后，对这些实体做一阶实体扩展
        List<HashMap<String, String>> commonEnt = commonEntInPair.get(0);
        int numCommonEnts = commonEnt.size();
        List<ArrayList<HashMap<String, String>>> commonNeighPaths = new ArrayList<>();
        for ( i = 0; i < numCommonEnts; i++ ) {
            String[] emptyString = {};
            List<ArrayList<HashMap<String, String>>> pathFromCommonEnt =
                    entityExtend(g, commonEnt.get(i).get("identity"), 1, emptyString, "identity",true);
            if ( pathFromCommonEnt == null ) {
                throw new Exception("--实体扩展错误");
            }
            int numPaths = pathFromCommonEnt.size();
            for ( j = 0; j < numPaths; j++ ) {
                if ( pathFromCommonEnt.get(j).size() != 3  // 获得的是实体的一阶实体扩展，即“点-边-点”，所以长度为3
                        || !pathFromCommonEnt.get(j).get(2).containsKey("parentType")
                        || !"entity".equals(pathFromCommonEnt.get(j).get(2).get("parentType")) ) {
                    throw new Exception("--实体扩展结果获取错误");
                } else {
                    if ( !pathFromCommonEnt.get(j).get(2).containsKey("identity") ) {
                        throw new Exception("--实体缺少id属性");
                    } else {
                        for ( k = 0; k < numVertices; k++ ) {
                            if ( vertexIds.get(k).equals(pathFromCommonEnt.get(j).get(2).get("identity")) ) {
                                commonNeighPaths.add(pathFromCommonEnt.get(j));
                                break;
                            }
                        }
                    }
                }
            }
        }
        return commonNeighPaths;
    }

    /**
     * 从关系推演的结果中获得两实体的共同邻居实体
     * @param common
     * @return 共同邻居实体组成的数组
     */
    private ArrayList<HashMap<String, String>> getEntityFromRelDed(List<ArrayList<HashMap<String, String>>> common)
            throws Exception {
        ArrayList<HashMap<String, String>> commonEntInPairHere = new ArrayList<>();
        int numPaths = common.size();
        int j;
        for ( j = 0; j < numPaths; j++ ) {
            if ( common.get(j).size() != 5  // 获得的是两实体的二阶关系推演，即“点-边-点-边-点”，所以长度为5
                    || !common.get(j).get(2).containsKey("parentType")  // 为区分实体和边而引入的父类型
                    || !"entity".equals(common.get(j).get(2).get("parentType")) ) {
                throw new Exception("--关系推演结果获取错误");
            } else {
                commonEntInPairHere.add(common.get(j).get(2));
            }
        }
        return commonEntInPairHere;
    }

    /**
     * 求两列表的交集
     * @param list1
     * @param list2
     * @return 交集数组
     */
    private ArrayList<HashMap<String, String>> getCommon(List<HashMap<String, String>> list1, List<HashMap<String, String>> list2)
            throws Exception {
        ArrayList<HashMap<String, String>> listCommon = new ArrayList<>();
        int i, j;
        for ( i = 0; i < list1.size(); i++ ) {
            for ( j = 0; j < list2.size(); j++ ) {
                if ( !list1.get(i).containsKey("identity") || !list2.get(j).containsKey("identity") ) {
                    throw new Exception("--共同邻居实体缺少id属性");
                } else if ( list1.get(i).get("identity").equals(list2.get(j).get("identity")) ) {
                    listCommon.add(list1.get(i));
                    break;
                }
            }
        }
        return listCommon;
    }



    public Vertex getVertexByKV(GraphTraversalSource g, String key, String value){
        List<Vertex> result = new ArrayList<>();
        GraphTraversal<Vertex, Vertex> traversal=  g.V().has(key,value);
        while(traversal.hasNext()){
            result.add(traversal.next());
        }
//        if(result.size()>1){
//            logger.info("查询到实体的结果：");
//            result.stream().forEach(vertex -> {
//                logger.info("实体类型："+vertex.label()+" 实体id:"+vertex.toString());
//            });
//        }
        if(result.size() < 1){
            throw new BuiltinSearchException(CommonResult.ERROR,
                    String.format("%s = %s的实体，在后台查询到零个VertexId",key,value));
        }else {
            logger.warn("{}={}的实体，在后台查到{}个VertexId",key,value,result.size());
        }
//        if(result.size() > 1){
//            throw new BuiltinSearchException(CommonResult.ERROR,
//                    String.format("%s = %s的实体，在后台查询到%s个VertexId",key,value,result.size()));
//        }
        return result.get(0);
    }
}
